package com.anjie.powerproject.service.impl;

import com.anjie.powerproject.entity.*;
import com.anjie.powerproject.repository.*;
import com.anjie.powerproject.service.DeviceService;
import com.anjie.powerproject.service.GraphInfoService;
import com.anjie.powerproject.service.RelationshipComService;
import com.anjie.powerproject.service.RelationshipService;
import com.anjie.powerproject.strategy.utils.DateUtils;
import org.dom4j.*;
import org.hibernate.StaleObjectStateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.util.*;

@Service
public class GraphInfoServiceImpl implements GraphInfoService {

    @Autowired
    private GraphInfoRepository graphInfoRepository;

    @Autowired
    private RelationshipRepository relationshipRepository;

    @Autowired
    private DeviceRepository deviceRepository;

    @Autowired
    private EnergyTypeRepository energyTypeRepository;

    @Autowired
    private ElectricityRepository electricityRepository;

    @Autowired
    private DeviceService deviceService;

    @Autowired
    private RelationshipService relationshipService;

    @Autowired
    private GraphDevServiceImpl graphDevService;

    @Autowired
    private GraphDevRepository graphDevRepository;

    @Autowired
    private RelationshipComService relationshipComService;

    @Override
    public List<GraphInfo> getAllGraphInfo() {
        return graphInfoRepository.findAll();
    }

    @Override
    public GraphInfo addOneGraphInfo(GraphInfo graphInfo) {
        return graphInfoRepository.saveAndFlush(graphInfo);
    }

    @Override
    public List<GraphInfo> findGraphInfoByUseRule(Integer useRule) {
        return graphInfoRepository.findAllByUseRule(useRule);
    }

    @Override
    public GraphInfo findGraphInfoById(Integer id) {
        return graphInfoRepository.findById(id).orElse(null);
    }

    @Override
    public GraphInfo saveAndFlush(GraphInfo graphInfo) {
        return graphInfoRepository.save(graphInfo);
    }

    @Override
    public void deleteGraphInfoByGraphId(Integer graphId) {
        graphInfoRepository.deleteById(graphId);
    }


    /**
     * 要保证该操作多线程安全, 事务的隔离级别设置为提交读（防止脏读、不可重复读、幻读）
     * @param graphId
     * @param xmlString
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
    public String updateGraphByXmlString(Integer graphId, String xmlString) {
        Map<String, List> map = readStringXml(xmlString, graphId);
        StringBuffer stringBuffer = new StringBuffer();
        try {
            if (map==null){
                stringBuffer.append("edgeError");
            } else {
                List<GraphDev> graphDevList = map.get("graphDev");

                List<Relationship> relationshipList = map.get("relationship");

                HashSet<Integer> relIDList = new HashSet<>();//用于存放图谱现有的边的id以及新增边的id,据此删除边
                HashSet<Integer> graIDList = new HashSet<>();

                //获取当前图谱的所有边信息
                List<Relationship> resultList = relationshipRepository.findRelationshipsByGraphIdEquals(graphId);
                List<Relationship> relationshipsDelete = new ArrayList<>();

                if(relationshipList != null && relationshipList.size() > 0){
                    List<Relationship> relationshipsAdd = new ArrayList<>();
                    for(Relationship r:relationshipList) {
                        //根据源设备、目标设备、图谱id确定边是否存在数据表中
                        Relationship relationship = relationshipService.findRelationshipByFromAndToAndGraphId(r);
                        //不存在则将这条数据插入数据表中
                        if (relationship == null) {
                            r.setLossRate(1);
                            //Relationship rel = relationshipRepository.saveAndFlush(r);
                            //relIDList.add(rel.getId());
                            relationshipsAdd.add(r);
                        } else {
                            relIDList.add(relationship.getId());
                        }
                    }
                    //获取当前图谱的所有边信息，根据idList删除其中不存在的边，完成删除操作
                    if(resultList != null && resultList.size() > 0){
                        for (Relationship r:resultList){
                            if(!relIDList.contains(r.getId())){
                                boolean flag = relationshipComService.findRelationshipComsByDeviceFromAndDeviceTo(r.getDeviceFrom(),r.getDeviceTo(),r.getGraphId());
                                if(flag){
                                    stringBuffer.append("fail");
                                    return stringBuffer.toString();
                                }
                                //relationshipRepository.deleteById(r.getId());
                                relationshipsDelete.add(r);
                            }
                        }
                    }
                    relationshipService.saveRelationshipList(relationshipsAdd);
                }else {//当将基础策略全部删除时判断
                    if(resultList != null && resultList.size() > 0){
                        for (Relationship r:resultList){
                            boolean flag = relationshipComService.findRelationshipComsByDeviceFromAndDeviceTo(r.getDeviceFrom(),r.getDeviceTo(),r.getGraphId());
                            if(flag){
                                stringBuffer.append("fail");
                                return stringBuffer.toString();
                            }
                            relationshipsDelete.add(r);
                        }
                    }
                    //relationshipRepository.deleteRelationshipsByGraphId(graphId);
                }
                //删除
                relationshipService.deleteRelationshipList(relationshipsDelete);
                //对设备及坐标位的处理保持同样的逻辑
                if(graphDevList != null && graphDevList.size() > 0){
                    for (GraphDev g:graphDevList){
                        GraphDev graphDev = graphDevService.findGraphDevsByDeviceIdAndGraphId(g);
                        if(graphDev == null){ //新增
                            GraphDev gra = graphDevRepository.saveAndFlush(g);
                            graIDList.add(gra.getId());
                        } else { //修改
                            int res = graphDevRepository.updateDeviceCoordinates(g.getxCoordinates(),g.getyCoordinates(),g.getDeviceId(),g.getGraphId(),graphDev.getVersion());
                            if (res == 0) {
                                throw new RuntimeException("不允许并发修改");
                            }
                            graIDList.add(graphDev.getId());
                        }
                    }
                    List<GraphDev> resultList1 = graphDevService.findGraphInfosByGraphId(graphId);
                    if(resultList1 != null && resultList1.size() > 0){
                        for (GraphDev g:resultList1){
                            if(!graIDList.contains(g.getId())){//删除
                                graphDevRepository.deleteById(g.getId());
                            }
                        }
                    }
                } else {
                    graphDevRepository.deleteGraphDevsByGraphId(graphId);
                }
                stringBuffer.append("success");
            }
        } catch (StaleObjectStateException e) {
            throw new RuntimeException("不允许多进程修改");
        }
        return stringBuffer.toString();
    }

    @Override
    @Transactional
    public void deleteAllGraphInfoByGraphId(Integer graphId) {
        graphDevService.deleteGraphDevsByGraphId(graphId);
        relationshipService.deleteRelationshipByGraphId(graphId);
        electricityRepository.deleteElectricitiesByGraphId(graphId);
        deleteGraphInfoByGraphId(graphId);
    }

    @Override
    public void updateStrategyTypeByGraphId(Integer strategyType,Integer graphId){
        graphInfoRepository.updateStrategyTypeByGraphId(strategyType, graphId);
    }

    public Map<String, List> readStringXml(String xml, Integer graphId) {
        Map<String,List> map = new HashMap<>();
        Document doc = null;
        List<Relationship> list1 = new ArrayList<>();
        List<GraphDev> graphDevs = new ArrayList<>();
        try {
            doc = DocumentHelper.parseText(xml); // 将字符串转为XML
            Element mxGraphModel = doc.getRootElement(); //获取根节点
            Element root = mxGraphModel.element("root");
            List<Element> childElements = root.elements();
            for (Element child : childElements) {
                //未知属性名情况下
            /*List<Attribute> attributeList = child.attributes();
            for (Attribute attr : attributeList) {
                System.out.println(attr.getName() + ": " + attr.getValue());
            }*/
                Attribute vertex = child.attribute("vertex");
                Attribute edge = child.attribute("edge");
                //确定是设备
                if (vertex!=null&&!"".equals(vertex.getValue())){
                    GraphDev graphDev = new GraphDev();
                    graphDev.setGraphId(graphId);
                    String id = child.attribute("id").getValue();
                    Element mxGeometry = child.element("mxGeometry");
                    if (mxGeometry.attribute("x")!=null) {
                        String x = mxGeometry.attribute("x").getValue();
                        graphDev.setxCoordinates(Float.valueOf(x));
                    }else {
                        graphDev.setxCoordinates(0);
                    }
                    graphDev.setDeviceId(Integer.parseInt(id));
                    if (mxGeometry.attribute("y")!=null) {
                        String y = mxGeometry.attribute("y").getValue();
                        graphDev.setyCoordinates(Float.valueOf(y));
                    }else {
                        graphDev.setyCoordinates(0);
                    }
                    graphDevs.add(graphDev);
                    //System.out.println("解析xml串：id : " + id+"  x : "+graphDev.getxCoordinates()+"  y : "+graphDev.getyCoordinates());
                }else if (edge!=null&&!"".equals(edge.getValue())){
                    Attribute source = child.attribute("source");
                    Attribute target = child.attribute("target");
                    if (source==null||target==null){
                        return null;
                    }else {
                        Integer from = Integer.valueOf(source.getValue());
                        Integer to = Integer.valueOf(target.getValue());
                        String value = child.attribute("value").getValue();
                        EnergyType energyTypeByTypeName = energyTypeRepository.findEnergyTypeByTypeName(value);
                        Relationship relationship = new Relationship();
                        relationship.setDeviceFrom(from);
                        relationship.setDeviceTo(to);
                        relationship.setTypeId(energyTypeByTypeName.getTypeId());
                        relationship.setGraphId(graphId);
                        list1.add(relationship);
                    }
                }
            }
        }catch(DocumentException e){
            e.printStackTrace();
        }
        map.put("graphDev",graphDevs);
        map.put("relationship",list1);
        return map;
    }

    // 添加图谱时的时间判断
    @Override
    public boolean checkTime(String startTime, String endTime) throws ParseException {
        // 比较之前拼接年份
        Date date = new Date();
        Date start = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + startTime);
        Date end = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + endTime);
        List<GraphInfo> all = graphInfoRepository.findAll();
        // 新增的时间不能涉足已占用的时间范围
        for (GraphInfo graphInfo: all) {
            Date tempStart = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + graphInfo.getGraphStartTime());
            tempStart = DateUtils.plusOrMinusDay(tempStart, -1).getTime();
            Date tempEnd = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + graphInfo.getGraphEndTime());
            tempEnd = DateUtils.plusOrMinusDay(tempEnd, 1).getTime();
            if (tempEnd.before(tempStart)) {
                Date tempMiddleL = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + "12-31");
                Date tempMiddleR = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + "01-01");
                if ((tempStart.before(start) && tempMiddleL.after(start)) || (tempStart.before(end) && tempMiddleL.after(end))) {
                    return false;
                }
                if ((tempMiddleR.before(start) && tempEnd.after(start)) || (tempMiddleR.before(end) && tempEnd.after(end))) {
                    return false;
                }
                if (start.equals(tempMiddleL) || start.equals(tempMiddleR) || end.equals(tempMiddleL) || end.equals(tempMiddleR)) {
                    return false;
                }
                // 这种情况下只有start和end也属于跨年情况时才有可能全部包含并重复
                if (end.before(start)) {
                    if (start.before(tempStart) && end.after(tempEnd)) {
                        return false;
                    }
                }
            } else {
                if ((tempStart.before(start) && tempEnd.after(start)) || (tempStart.before(end) && tempEnd.after(end))) {
                    return false;
                }
                // 一种额外的情况：start和end完全包含了tempStart和tempEnd，这里要分两种情况讨论
                // 1. end小于start，也就是说新增的图谱也是跨年的，那么就有可能左半边全包含，或者右半边全包含
                if (end.before(start)) {
                    Date middleL = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + "12-31");
                    Date middleR = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + "01-01");
                    if ((start.before(tempStart) && middleL.after(tempEnd)) || (middleR.before(tempStart) && end.after(tempEnd))) {
                        return false;
                    }
                    if (start.equals(tempStart) && middleL.equals(tempEnd) || middleR.equals(tempStart) && end.equals(tempEnd)) {
                        return false;
                    }
                } else {
                    // 2. end大于start，那么就只需要判断start和end是否包含了tempStart和tempEnd即可
                    if (start.before(tempStart) && end.after(tempEnd) || start.equals(tempStart) && end.equals(tempEnd)) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

//    // 修改图谱时的时间判断
//    @Override
//    public boolean checkTime2(String startTime, String endTime, Integer graphId) throws ParseException {
//        // 比较之前拼接年份
//        Date date = new Date();
//        Date start = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + startTime);
//        Date end = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + endTime);
//        Date middleL = null;
//        Date middleR = null;
//        if (end.before(start)) {
//            middleL = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + "12-31");
//            middleR = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + "01-01");
//        }
//        List<GraphInfo> all = graphInfoRepository.findAll();
//
//        for (GraphInfo graphInfo: all) {
//            // 跳过自己
//            if (graphInfo.getGraphId() == graphId) {
//                continue;
//            }
//            Date tempStart = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + graphInfo.getGraphStartTime());
//            tempStart = DateUtils.plusOrMinusDay(tempStart, -1).getTime();
//            Date tempEnd = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + graphInfo.getGraphEndTime());
//            tempEnd = DateUtils.plusOrMinusDay(tempEnd, -1).getTime();
//            if (middleL != null) {
//                // tempStart和tempEnd处于start右边，middle左边
//                if (start.before(tempStart) && middleL.after(tempStart) && start.before(tempEnd) && middleL.after(tempEnd)) {
//                    return false;
//                }
//                // tempStart和tempEnd处于middle右边，end左边
//                if (middleR.before(tempStart) && end.after(tempStart) && middleR.before(tempEnd) && end.after(tempEnd)) {
//                    return false;
//                }
//                // tempStart处于start右边middle左边，tempEnd处于end左边middle右边
//                if (start.before(tempStart) && middleL.after(tempStart) && middleR.before(tempEnd) && end.after(tempEnd)) {
//                    return false;
//                }
//            } else {
//                if (start.before(tempStart) && end.after(tempStart) && start.before(tempEnd) && end.after(tempEnd)) {
//                    return false;
//                }
//            }
//        }
//        return true;
//    }

    // 修改图谱时的时间判断
    @Override
    public boolean checkTime2(String startTime, String endTime, Integer graphId) throws ParseException {
        // 比较之前拼接年份
        Date date = new Date();
        Date start = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + startTime);
        Date end = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + endTime);
        List<GraphInfo> all = graphInfoRepository.findAll();
        // 新增的时间不能涉足已占用的时间范围
        for (GraphInfo graphInfo: all) {
//        	 跳过自己
            if (graphInfo.getGraphId().equals(graphId)) {
                continue;
            }
            Date tempStart = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + graphInfo.getGraphStartTime());
            tempStart = DateUtils.plusOrMinusDay(tempStart, -1).getTime();
            Date tempEnd = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + graphInfo.getGraphEndTime());
            tempEnd = DateUtils.plusOrMinusDay(tempEnd, 1).getTime();
            if (tempEnd.before(tempStart)) {
                Date tempMiddleL = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + "12-31");
                Date tempMiddleR = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + "01-01");
                if ((tempStart.before(start) && tempMiddleL.after(start)) || (tempStart.before(end) && tempMiddleL.after(end))) {
                    return false;
                }
                if ((tempMiddleR.before(start) && tempEnd.after(start)) || (tempMiddleR.before(end) && tempEnd.after(end))) {
                    return false;
                }
                if (start.equals(tempMiddleL) || start.equals(tempMiddleR) || end.equals(tempMiddleL) || end.equals(tempMiddleR)) {
                    return false;
                }
                // 这种情况下只有start和end也属于跨年情况时才有可能全部包含并重复
                if (end.before(start)) {
                    if (start.before(tempStart) && end.after(tempEnd)) {
                        return false;
                    }
                }
            } else {
                if ((tempStart.before(start) && tempEnd.after(start)) || (tempStart.before(end) && tempEnd.after(end))) {
                    return false;
                }
                // 一种额外的情况：start和end完全包含了tempStart和tempEnd，这里要分两种情况讨论
                // 1. end小于start，也就是说新增的图谱也是跨年的，那么就有可能左半边全包含，或者右半边全包含
                if (end.before(start)) {
                    Date middleL = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + "12-31");
                    Date middleR = DateUtils.ymdFormat.parse(1900 + date.getYear() + "-" + "01-01");
                    if ((start.before(tempStart) && middleL.after(tempEnd)) || (middleR.before(tempStart) && end.after(tempEnd))) {
                        return false;
                    }
                    if (start.equals(tempStart) && middleL.equals(tempEnd) || middleR.equals(tempStart) && end.equals(tempEnd)) {
                        return false;
                    }
                } else {
                    // 2. end大于start，那么就只需要判断start和end是否包含了tempStart和tempEnd即可
                    if (start.before(tempStart) && end.after(tempEnd) || start.equals(tempStart) && end.equals(tempEnd)) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    @Override
    public GraphInfo findBeforeGraphInfo(Date now) {
        String tempDateStr = DateUtils.mdFormat.format(now);
        GraphInfo beforeGraph = graphInfoRepository.findBeforeGraph(-1, tempDateStr);
        if (beforeGraph == null) {
            beforeGraph = graphInfoRepository.findBeforeGraph2(-1, tempDateStr);
        }
        return beforeGraph;
    }

    // 重定义更新图谱的方法，开启事务
    @Override
    @Transactional
    public GraphInfo updateGraphInfo(GraphInfo graphInfo) throws ParseException {
        // 1. 判断是否改变了图谱使用时间
        GraphInfo temp = graphInfoRepository.getOne(graphInfo.getGraphId());
        graphInfo.setStrategyType(temp.getStrategyType());

        // 若改变了开始时间或结束时间
        if (!temp.getGraphStartTime().equals(graphInfo.getGraphStartTime()) || !temp.getGraphEndTime().equals(graphInfo.getGraphEndTime())) {
            if (!temp.getGraphStartTime().equals(graphInfo.getGraphStartTime())) {
                // 寻找该图谱的前一个图谱
                GraphInfo beforeGraph = graphInfoRepository.findBeforeGraph(graphInfo.getGraphId(), temp.getGraphStartTime());
                if (beforeGraph == null) {
                    beforeGraph = graphInfoRepository.findBeforeGraph2(graphInfo.getGraphId(), temp.getGraphStartTime());
                }

                if (beforeGraph != null) {
                    // 如果前一个图谱与本图谱相连, 则无论本图谱时间如何改变，上一个图谱的结束时间必须跟着改变
                    Date beforeGraphEndTime = DateUtils.mdFormat.parse(beforeGraph.getGraphEndTime());
                    Date beforeGraphStartTime = DateUtils.mdFormat.parse(beforeGraph.getGraphStartTime());
                    Date tempStartTime = DateUtils.plusOrMinusDay(DateUtils.mdFormat.parse(temp.getGraphStartTime()), -1).getTime();
                    Date newStartTime = DateUtils.plusOrMinusDay(DateUtils.mdFormat.parse(graphInfo.getGraphStartTime()), -1).getTime();

                    System.out.println("before:" + beforeGraph);
                    System.out.println(DateUtils.ymdFormat.format(beforeGraphEndTime));
                    System.out.println(DateUtils.ymdFormat.format(tempStartTime));

                    if (beforeGraphEndTime.equals(tempStartTime)) {
                        beforeGraph.setGraphEndTime(DateUtils.mdFormat.format(newStartTime));
                        graphInfoRepository.saveAndFlush(beforeGraph);
                    } else {
                        // 若原本前一个图谱与本图谱不相连，但是现在修改后的图谱开始时间早于，或等于前一个图谱的结束时间，则还是要修改上一个图谱的结束时间
                        if (newStartTime.before(beforeGraphEndTime) && newStartTime.after(beforeGraphStartTime)) {
                            beforeGraph.setGraphEndTime(DateUtils.mdFormat.format(newStartTime));
                            graphInfoRepository.saveAndFlush(beforeGraph);
                        }
                    }
                }
            }
            if (!temp.getGraphEndTime().equals(graphInfo.getGraphEndTime())) {
                // 寻找该图谱的后一个图谱
                GraphInfo afterGraph = graphInfoRepository.findAfterGraph(graphInfo.getGraphId(), temp.getGraphEndTime());
                if (afterGraph == null) {
                    afterGraph = graphInfoRepository.findAfterGraph2(graphInfo.getGraphId(), temp.getGraphEndTime());
                }

                if (afterGraph != null) {
                    // 如果后一个图谱与本图谱相连
                    Date afterGraphStartTime = DateUtils.mdFormat.parse(afterGraph.getGraphStartTime());
                    Date afterGraphEndTime = DateUtils.mdFormat.parse(afterGraph.getGraphEndTime());
                    Date tempEndTime = DateUtils.plusOrMinusDay(DateUtils.mdFormat.parse(temp.getGraphEndTime()), 1).getTime();
                    Date newEndTime = DateUtils.plusOrMinusDay(DateUtils.mdFormat.parse(graphInfo.getGraphEndTime()), 1).getTime();

                    System.out.println("after:" + afterGraph);
                    System.out.println(DateUtils.ymdFormat.format(afterGraphStartTime));
                    System.out.println(DateUtils.ymdFormat.format(tempEndTime));
                    if (afterGraphStartTime.equals(tempEndTime)) {
                        afterGraph.setGraphStartTime(DateUtils.mdFormat.format(newEndTime));
                        graphInfoRepository.saveAndFlush(afterGraph);
                    } else {
                        // 若原本前一个图谱与本图谱不相连，但是现在修改后的图谱开始时间早于，或等于前一个图谱的结束时间，则还是要修改上一个图谱的结束时间
                        if (newEndTime.after(afterGraphStartTime) && newEndTime.before(afterGraphEndTime)) {
                            afterGraph.setGraphStartTime(DateUtils.mdFormat.format(newEndTime));
                            graphInfoRepository.saveAndFlush(afterGraph);
                        }
                    }
                }
            }
            // 最后更新本身
            return graphInfoRepository.saveAndFlush(graphInfo);
        } else {
            // 否则直接调用saveAndFlush
            return graphInfoRepository.saveAndFlush(graphInfo);
        }
    }
}
